// ============================================================================
//
// Copyright (C) 2006-2007 Dengues
//
// Google Group: http://groups.google.com/group/dengues
// QQ Group: 24885404
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// ============================================================================
package org.dengues.designer.core.sqldb;

import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;

import org.dengues.core.DenguesCorePlugin;
import org.dengues.core.resource.DenguesProjectFactory;
import org.dengues.designer.core.launch.DenguesProcessFactory;
import org.dengues.designer.core.launch.DenguesRuntimeProcess;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IDebugEventSetListener;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;

/**
 * Qiang.Zhang.Adolf@gmail.com class global comment. Detailled comment <br/>
 * 
 * $Id: Dengues.epf 1 2006-09-29 17:06:40Z qiang.zhang $ Singleton Class to keep track of HSQLDB Server instances
 * started by the plug-in
 */
public class SqldbServerTracker {

    public static final String HSQL_DATA_DIR = "/data"; //$NON-NLS-1$

    private static SqldbServerTracker theOnlyInstance = new SqldbServerTracker();

    private final HashMap<IProcess, IJavaProject> servers = new HashMap<IProcess, IJavaProject>();

    /**
     * Qiang.Zhang.Adolf@gmail.com SqldbServerTracker constructor comment.
     */
    private SqldbServerTracker() {
        super();
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "getDefault".
     * 
     * @return
     */
    public static SqldbServerTracker getDefault() {
        if (theOnlyInstance == null)
            theOnlyInstance = new SqldbServerTracker();
        return theOnlyInstance;
    }

    // listener for DebugEvents, so we know if a server was stopped by a client
    // or died by itself

    private final IDebugEventSetListener listener = new IDebugEventSetListener() {

        /*
         * (non-Javadoc)
         * 
         * @see org.eclipse.debug.core.IDebugEventSetListener#handleDebugEvents(org.eclipse.debug.core.DebugEvent[])
         */
        public void handleDebugEvents(DebugEvent[] events) {
            // if the event was a terminate...
            if (events[0].getKind() == DebugEvent.TERMINATE) {
                Object source = events[0].getSource();
                if (source instanceof IProcess) {
                    // ...and the terminated process is a HSQLDB Server...
                    Object proj = servers.get(source);
                    if (proj != null) {
                        try {
                            // ...remove it from the map and update the ui
                            servers.remove(source);
                            setTrminate();
                        } catch (CoreException ce) {
                            System.err.println(SqldbUtil.getStatusMessages(ce));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    };

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "setTrminate".
     * 
     * @throws CoreException
     * @throws ClassNotFoundException
     * @throws SQLException
     */
    public void setTrminate() throws CoreException, ClassNotFoundException, SQLException {
        StartHsqldbServer server = (StartHsqldbServer) DenguesCorePlugin.getDefault().getDesignerCoreService().createStartHsqldbServer();
        server.update(false);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "startHsqldbServer".
     * 
     * @param proj
     * @throws CoreException
     */
    public void startHsqldbServer(IJavaProject proj) throws CoreException {
        // build server command-line
        SqldbParams params = new SqldbParams();
        String database = addHsqlEngine(proj);
        String args = "-database " + database + " " + "-port " + params.getPort(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
        // starts the server as a Java app
        ILaunch launch = DenguesProcessFactory.launch(proj, DenguesProjectFactory.getDenguesProject().getName().toLowerCase()
                + " (" + proj.getElementName() + ")", params.getServerClass(), args); //$NON-NLS-1$ //$NON-NLS-2$
        // saves the mapping between (server) process and project
        IProcess[] processes = launch.getProcesses();
        for (IProcess process : processes) {
            if (process instanceof DenguesRuntimeProcess) {
                DenguesRuntimeProcess runtimeProcess = (DenguesRuntimeProcess) process;
                // System.out.println("Errors: " + runtimeProcess.getErrorContents());
                // System.out.println("Input: " + runtimeProcess.getInputContents());
            }

        }
        if (processes.length > 0) {
            servers.put(processes[0], proj);
        }
        // register a listener so we know when this process is finished
        DebugPlugin debug = DebugPlugin.getDefault();
        debug.addDebugEventListener(listener);
        // so the UI will reflect there's already a server running
        // setRunning(proj, Boolean.TRUE);
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "addHsqlEngine".
     * 
     * @param proj
     * @return
     */
    private String addHsqlEngine(IJavaProject proj) {
        IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
        Cursor waitCursor = new Cursor(window.getShell().getDisplay(), SWT.CURSOR_WAIT);
        String dbPath = proj.getProject().getLocation() + HSQL_DATA_DIR;
        File dbDir = new File(dbPath);
        if (!dbDir.exists()) {
            dbDir.mkdir();
        }
        String database = dbPath + "/" + DenguesProjectFactory.getDenguesProject().getName().toLowerCase(); //$NON-NLS-1$
        try {
            window.getShell().setCursor(waitCursor);
            IProject project = proj.getProject();
            // create the initial database files (what name?)
            // HsqldbUtil.createEmptyHsqlDatabase(database);
            // refresh project so user sees new files, libraries, etc
            project.refreshLocal(IResource.DEPTH_INFINITE, null);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            window.getShell().setCursor(null);
            waitCursor.dispose();
        }
        return database;
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "stopHsqldbServer".
     * 
     * @throws CoreException
     * @throws ClassNotFoundException
     * @throws SQLException
     */
    public void stopHsqldbServer() throws CoreException, ClassNotFoundException, SQLException {
        Connection con = getCurrentConn();
        String sql = "SHUTDOWN"; //$NON-NLS-1$
        Statement stmt = con.createStatement();
        stmt.executeUpdate(sql);
        stmt.close();
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "getCurrentConn".
     * 
     * @param proj
     * @return
     * @throws CoreException
     * @throws ClassNotFoundException
     * @throws SQLException
     */
    public Connection getCurrentConn() throws CoreException, ClassNotFoundException, SQLException {
        SqldbParams params = new SqldbParams();
        // submits the SHUTDOWN statement
        Class.forName(params.getDriver());
        String url = "jdbc:hsqldb:hsql://127.0.0.1:" + params.getPort(); //$NON-NLS-1$
        Connection con = DriverManager.getConnection(url, params.getUser(), params.getPassword());
        return con;
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "shutdownAllServers".
     */
    public void shutdownAllServers() {
        try {
            stopHsqldbServer();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
